#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#include "network.h"
#include "request.h"
#include "database.h"
#include "process.h"

#define BUF_SIZE 512

int request_type (char *req) {

	if (strcmp("announce", strtok(req," ")) == 0) {
		return ANNOUNCE;
	}
	else if (strcmp("look", strtok(req," ")) == 0) {
		return LOOK;
	}
	else if (strcmp("getfile", strtok(req," ")) == 0) {
		return GETFILE;
	}
	else if (strcmp("update", strtok(req," ")) == 0) {
		return UPDATE;
	}

	return -1;
};

/*****************************/
/***** LOOK FUNCTIONS *****/
/*****************************/

struct criterion ** new_criterion_tab () {

	struct criterion ** tab = malloc (sizeof (struct criterion *));
	tab[0] = NULL;
	return tab;
}

struct criterion * new_criterion () {
	struct criterion * c = malloc (sizeof (struct criterion));
	c->entity = "";
	c->comp = '\0';
	c->value = "";
	return c;
}

int size_tab (struct criterion ** tab) {
	int i = 0;
	while (tab[i] != NULL)
		i++;
	return i+1;
}

void free_criterion_list (struct criterion ** ls) {
    int i;

    for (i=0; i<size_tab(ls) && ls[i] != NULL; i++) {
        free (ls[i]->value);
        free (ls[i]->entity);
        free (ls[i]);
    }
}

// SAVING CRITERION FIELDS
struct criterion ** add_criterion (struct criterion ** ls, struct criterion * c) {
	int last, before_last;

	ls = realloc (ls, (size_tab(ls) + 1) * sizeof (struct criterion *));

	last  = size_tab(ls);
	before_last = last - 1;

	ls[before_last] = c;
	ls[last] = NULL;

	return ls;
}

struct criterion * parse_criterion (struct criterion * c, char * elt) {
	char * pch;
	char * toparse;

	toparse = malloc ((strlen (elt) +1) * sizeof (char));
	strcpy (toparse, elt);

	pch = strtok (elt,"\"");
	c->comp = last_char(pch);
	c->entity = malloc (sizeof (char) * (strlen(pch)));
	strcpy (c->entity, remove_last_char (pch));

	pch = strtok (NULL,"\"");
	c->value = malloc (sizeof (char) * (strlen(pch) + 1));
	strcpy (c->value, pch);

	return c;
}

// FIND FILES MATCHING CRITERIONS
char * look_with_criterions (char * rep, struct criterion ** ls) {

	if (db != NULL) {
		int i;
		struct peer *p;

		for (i=0; i<db->nb_peer; i++) {
			p = db->list_peer[i];
			if (p->list_file != NULL) {
				rep = strcpy (rep, check_files_of_peer_look (p, ls, rep));
			}
		}
	}
	if (last_char(rep) != '[')
    	strcpy(rep, remove_last_char (rep));
	return rep;
}

// COMPLETE THE RESPONSE IF THE FILE MATCHES
char * check_files_of_peer_look (struct peer *p, struct criterion ** ls, char *rep) {
	int j;
	struct file *f;
    char* tmp = malloc(sizeof(rep));

	for (j=0; j<p->nb_file; j++) {
		f = p->list_file[j];
		if (ls==NULL  || (check_criterion_file (ls, f))) {

		    int size = strlen(rep) + strlen(f->name) + strlen(f->size) + strlen(f->block_size) + strlen(f->key) + 4;
		    //rep = realloc (rep, sizeof(char) * size);

            strcpy (tmp, rep);
            rep = malloc (sizeof(char) * size);
            strcpy (rep, tmp);

			strcat (rep, f->name);
			strcat (rep, " ");
			strcat (rep, f->size);
			strcat (rep, " ");
			strcat (rep, f->block_size);
			strcat (rep, " ");
			strcat (rep, f->key);
			strcat (rep, " ");

		}
	}

	return rep;
}

// CHECK IF A FILE MATCHES EACH CRITERION
int check_criterion_file (struct criterion ** ls, struct file *f){
	int i;


	for (i=0; ls[i] != NULL; i++) {


		if (equal (ls[i]->entity, "filename")) {
			if (ls[i]->comp != '=') {
				fprintf(stderr, "Unauthorized comparison operator for filename");
			    return 0;
			}
			else if (equal (ls[i]->value, f->name) == 0)
				return 0;
		}
		else if (equal (ls[i]->entity, "filesize")) {
			if ((ls[i]->comp == '<') && !(atol(f->size) < atol (ls[i]->value)))
				return 0;
			else if ((ls[i]->comp == '>') && !(atol(f->size) > atol (ls[i]->value)))
				return 0;
			else if (!(ls[i]->comp == '>') && !(ls[i]->comp == '<')) {
				fprintf(stderr, "Unauthorized comparison operator for filesize");
                return 0;
            }
		}
		else return 0;
	}

	return 1;
}


/*****************************/
/***** GETFILE FUNCTIONS *****/
/*****************************/

//FIND PEERS HAVING KEY <key>
char * find_peers_getfile (char * key, char * rep) {
	int i;
	struct peer *p;
	for (i=0; i<db->nb_peer; i++) {
		p = db->list_peer[i];
		if (p->list_file != NULL) {
   			rep = strcpy (rep, check_peer_key (p, key, rep));			}

	}

	return rep;
}

//CHECK IF A PEER HAS FILE WHOM KEY IS <key> AND COMPLETE THE RESPONSE
char * check_peer_key (struct peer * p, char * key, char * rep){

   	int found, k;
	struct file *f;
    char* tmp = malloc(sizeof (char) * (strlen (rep) + 1));

	found = 0;
	for (k=0; k<p->nb_file && found != 1; k++) {
		f = p->list_file[k];
		if (equal (key, f->key) /*&& is_seed (p, key)*/) {
				found = 1;

		    int size = strlen (rep) + strlen (p->ip) + strlen (p->port) + 3;
            strcpy (tmp, rep);

            rep = malloc (sizeof(char) * size);
            strcpy (rep, tmp);

			strcat (rep, p->ip);
			strcat (rep, ":");
			strcat (rep, p->port);
			strcat (rep, " ");
		}
	}

	if (found == 1) rep = remove_last_char(rep);

	return rep;
}


/*****************************/
/***** UPDATE FUNCTIONS ******/
/*****************************/
int is_leech (struct peer * p, char * key) {
	int i;
	struct file * f;

	if (p->list_file != NULL) {
		for (i=0; i<p->nb_file; i++) {
			f = p->list_file[i];
			if (equal (f->key, key) && f->is_seed == 0)
				return 1;
		}
	}
	return 0;
}

int is_seed (struct peer * p, char * key) {
	int i;
	struct file * f;

	if (p->list_file != NULL) {
		for (i=0; i<p->nb_file; i++) {
			f = p->list_file[i];
			if (equal (f->key, key) && f->is_seed == 1)
				return 1;
		}
	}
	return 0;
}

void update_seed (struct peer * p_courant, struct peer * p_new, char * key) {

	struct file *f, *tmp_f;

	if (is_leech(p_courant, key) || is_seed(p_courant, key)) {
		tmp_f = find_file (key);
		if (tmp_f == NULL) perror ("file not seeded\n");
		else {
			f = create_file (tmp_f->name, tmp_f->size, tmp_f->block_size, tmp_f->key, 1);
			add_file_to_peer (f, p_new);
		}
	}
	else perror("A file has to be either seed or leech to turn seed\n");
}


void update_leech (struct peer * p_new, char * key) {
	struct file * f;

	f = create_file ("-", "-", "-", key, 0);
	add_file_to_peer (f, p_new);
}


void replace_peers (struct peer * p_curent, struct peer * p_new) {
	int i;

	for (i=0; i<db->nb_peer; i++) {
		if (db->list_peer[i] == p_curent) {
			db->list_peer[i] = p_new;
			free (p_curent);
			break;
		}
	}
}

/*****************************************/
/***** STRING MANIPULATION FUNCTIONS *****/
/*****************************************/

int equal (char *str1, char *str) {
	if (strcmp(str1, str) == 0)
		return 1;
	return 0;
}

char first_char (char *str) {
	return str[0];
}

char last_char (char *str) {
	return str[strlen(str)-1];
}

char *remove_first_char (char *str) {
	unsigned int i, j=0;
	char *res = malloc(strlen(str));
	for (i=1; i<strlen(str); i++) {
		res[j++] = str[i];
	}
	res[j]='\0';
	return res;
}

char *remove_last_char (char *str) {
	unsigned int i, j=0;
	char *res = malloc(strlen(str));
	for (i=0; i<strlen(str)-1; i++) {
		res[j++] = str[i];
	}
	res[j]='\0';
	return res;
}

char **split (char *str) {

	char **res = NULL;
	char * pch = strtok (str, " ");
	int k=0;
	while (pch != NULL) {
		k++;
		res = realloc(res, sizeof(char*) * k);
		res[k-1] = malloc(strlen(pch) + 1);
		strcpy(res[k-1], pch);
		pch = strtok (NULL, " ");
	}
	k++;
	res = realloc(res, sizeof(char*) * k);
	res[k-1] = NULL;
	return res;
}
